﻿#ifndef __OC_StringUtil_H__
#define __OC_StringUtil_H__

#include "Prerequisites.h"

namespace OCPP
{
    class _MainExport StringUtil
    {
    public:
        static const String GenGUID();
        static const String hexEncode(String str);
        static const String hexDecode(String str);

        static const String encodeBase64(const uint8* in,uint32 inlen);
        static const String encodeBase64(String str);
        static const String decodeBase64(String str);

        static const String encryptAES(String key, String data);
        static const String decryptAES(String key, String data);

        static const String replaceAll(
            const String& source,
            const String& replaceWhat,
            const String& replaceWithWhat);

        static bool startsWith(
            const String& ref, 
            const String& pattern,
            bool caseSensitive = true);
        static bool endsWith(
            const String& ref,
            const String& pattern,
            bool caseSensitive = true);

        static StringVector split(const String& str,
            const String& delims = "\t\n ", 
            unsigned int maxSplits = 0,
            bool preserveDelims = false);

        static void splitFilename(const String& qualifiedName,
            String& outBasename, String& outPath);

        static void splitFullFilename(const String& qualifiedName,
            String& outBasename, String& outExtention,
            String& outPath);

        static void splitBaseFilename(const String& fullName,
            String& outBasename, String& outExtention);

        static void trim(String& str, bool left = true, bool right = true, String delims = " \t\r");
        static void toUpperCase(String& str);
        static void toLowerCase(String& str);
        
        static String ANSIToUTF8(const String str);
        static String UTF8ToANSI(const String& s);
        static WString UTF8toUTF16(const String str);

        static double asciiToDouble(const char* str);

        static float asciiToFloat(const char* str);

        /** Detect first ascii POSITIVE number in string and convert to double.*/
        static double findAsciiToDouble(const char* str);

        static bool contains(const String& str, const String& keyword, bool caseSensitive = true);

        static const String getTime();

        /** TimeStr format 2023-07-15 12:00:00  */
        static const time_t parseTime(String timeStr);
    };

    struct Stringify
    {
        operator String() const
        {
            String result;
            result = buf.str();
            return result;
        }

        template<typename T>
        Stringify& operator << (const T& val) { buf << val; return (*this); }

        Stringify& operator << (const Stringify& val) { buf << (String)val; return (*this); }

    protected:
        StringStream buf;
    };

    template<> inline
        Stringify& Stringify::operator << <bool>(const bool& val) { buf << (val ? "true" : "false"); return (*this); }

    template<typename ... Args>
    static String str_format(const String& format, Args ... args)
    {
        auto size_buf = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1;
        std::unique_ptr<char[]> buf(new(std::nothrow) char[size_buf]);

        if (!buf)
            return String("");

        std::snprintf(buf.get(), size_buf, format.c_str(), args ...);
        return String(buf.get(), buf.get() + size_buf - 1);
    }

    #ifndef _WeWasm
    template<typename ... Args>
    static WString wstr_format(const WString& format, Args ... args)
    {
        auto size_buf = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1;
        std::unique_ptr<char[]> buf(new(std::nothrow) char[size_buf]);

        if (!buf)
            return WString("");

        std::snprintf(buf.get(), size_buf, format.c_str(), args ...);
        return WString(buf.get(), buf.get() + size_buf - 1);
    }
    #endif

    inline String joinPath(const String& firstPath)
    {
        return firstPath;
    }

    template<typename... Args>
    inline String joinPath(const String& firstPath, Args... args)
    {
        String result = firstPath;
        if (!result.empty() && result.back() != '/')
        {
            result += '/';
        }
        result += joinPath(args...);
        return result;
    }

#define U2A(str) StringUtil::UTF8ToANSI(str)
#define A2U8(str) StringUtil::ANSIToUTF8(str)
}

#endif
